/*
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package hextd.tower;

import hextd.creep.HexTDCreep;
import hextd.creep.HexTDCreepData;
import java.awt.geom.Point2D.Double;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;

public class CreepCompare {

	public static class CompareFast implements Comparator<HexTDCreep> {

		@Override
		public int compare(HexTDCreep e1, HexTDCreep e2) {
			if (e1 == null && e2 == null) {
				return 0;
			}
			if (e1 == e2) {
				return 0;
			}
			if (e1 == null) {
				return -1;
			}
			if (e2 == null) {
				return 1;
			}

			double e1d, e2d;
			e1d = e1.getSpeed();
			e2d = e2.getSpeed();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			e1d = e1.getDistance();
			e2d = e2.getDistance();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			long e1l, e2l;
			e1l = e1.getHealth();
			e2l = e2.getHealth();
			if (e1l != e2l) {
				return (int) (e1l - e2l);
			}

			int e1i, e2i;
			e1i = e1.getPathId();
			e2i = e2.getPathId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			if (!e2.getType().equals(e1.getType())) {
				return HexTDCreepData.identifyEnemy(e1.getType()).compareTo(HexTDCreepData.identifyEnemy(e2.getType()));
			}

			e1i = e1.getOwner().getId();
			e2i = e2.getOwner().getId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			e1l = e1.getId();
			e2l = e2.getId();
			if (e1l != e2l) {
				return (int) (e1l - e2l);
			}
			System.out.println("CreepCompare::CompareFast:compare: creeps seem the same, but should not!");
			return -1;
		}
	}

	public static class CompareFurthest implements Comparator<HexTDCreep> {

		@Override
		public int compare(HexTDCreep e1, HexTDCreep e2) {
			if (e1 == null && e2 == null) {
				return 0;
			}
			if (e1 == e2) {
				return 0;
			}
			if (e1 == null) {
				return -1;
			}
			if (e2 == null) {
				return 1;
			}

			double e1d, e2d;
			e1d = e1.getDistance();
			e2d = e2.getDistance();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			e1d = e1.getSpeed();
			e2d = e2.getSpeed();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			long e1l, e2l;
			e1l = e1.getHealth();
			e2l = e2.getHealth();
			if (e2l != e1l) {
				return (int) (e2l - e1l);
			}

			int e1i, e2i;
			e1i = e1.getPathId();
			e2i = e2.getPathId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			if (!e2.getType().equals(e1.getType())) {
				return HexTDCreepData.identifyEnemy(e1.getType()).compareTo(HexTDCreepData.identifyEnemy(e2.getType()));
			}

			e1i = e1.getOwner().getId();
			e2i = e2.getOwner().getId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			e1l = e1.getId();
			e2l = e2.getId();
			if (e1l != e2l) {
				return (int) (e1l - e2l);
			}

			System.out.println("CreepCompare::CompareFurthest:compare: creeps seem the same, but should not!");
			return -1;
		}
	}

	public static class CompareHard implements Comparator<HexTDCreep> {

		@Override
		public int compare(HexTDCreep e1, HexTDCreep e2) {
			if (e1 == null && e2 == null) {
				return 0;
			}
			if (e1 == e2) {
				return 0;
			}
			if (e1 == null) {
				return -1;
			}
			if (e2 == null) {
				return 1;
			}

			long e1l, e2l;
			e1l = e1.getHealth();
			e2l = e2.getHealth();
			if (e1l != e2l) {
				return (int) (e1l - e2l);
			}

			double e1d, e2d;
			e1d = e1.getDistance();
			e2d = e2.getDistance();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			e1d = e1.getSpeed();
			e2d = e2.getSpeed();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			int e1i, e2i;
			e1i = e1.getPathId();
			e2i = e2.getPathId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			if (!e2.getType().equals(e1.getType())) {
				return HexTDCreepData.identifyEnemy(e1.getType()).compareTo(HexTDCreepData.identifyEnemy(e2.getType()));
			}

			e1i = e1.getOwner().getId();
			e2i = e2.getOwner().getId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			e1l = e1.getId();
			e2l = e2.getId();
			if (e1l != e2l) {
				return (int) (e1l - e2l);
			}

			System.out.println("CreepCompare::CompareHard:compare: creeps seem the same, but should not!");
			return -1;
		}
	}

	public static class CompareWeak implements Comparator<HexTDCreep> {

		@Override
		public int compare(HexTDCreep e1, HexTDCreep e2) {
			if (e1 == null && e2 == null) {
				return 0;
			}
			if (e1 == e2) {
				return 0;
			}
			if (e1 == null) {
				return -1;
			}
			if (e2 == null) {
				return 1;
			}

			long e1l, e2l;
			e1l = e1.getHealth();
			e2l = e2.getHealth();
			if (e1l != e2l) {
				return (int) (e2l - e1l);
			}

			double e1d, e2d;
			e1d = e1.getDistance();
			e2d = e2.getDistance();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			e1d = e1.getSpeed();
			e2d = e2.getSpeed();
			if (e1d != e2d) {
				return java.lang.Double.compare(e1d, e2d);
			}

			int e1i, e2i;
			e1i = e1.getPathId();
			e2i = e2.getPathId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			if (!e2.getType().equals(e1.getType())) {
				return HexTDCreepData.identifyEnemy(e1.getType()).compareTo(HexTDCreepData.identifyEnemy(e2.getType()));
			}

			e1i = e1.getOwner().getId();
			e2i = e2.getOwner().getId();
			if (e1i != e2i) {
				return (int) (e1i - e2i);
			}

			e1l = e1.getId();
			e2l = e2.getId();
			if (e1l != e2l) {
				return (int) (e1l - e2l);
			}

			System.out.println("CreepCompare::CompareWeak:compare: creeps seem the same, but should not!");
			return -1;
		}
	}

	public static HexTDCreep[] findLargestSet(Collection<HexTDCreep> enemies, int number, double x, double y, double r, Comparator<HexTDCreep> c, HexTDCreep[] exclude, boolean exShielded) {
		HexTDCreep[] retval = new HexTDCreep[number];
		double r2 = r * r;
		double dx, dy;
		Double gameLocation = null;
		int foundCount = 0;
		int smallestPointer = number - 1;

		Arrays.sort(exclude, c);
		for (HexTDCreep e : enemies) {
			if (!e.isValidTarget() || (exShielded && e.isShielded())) {
				continue;
			}
			e.getGameLocation(gameLocation);
			dx = gameLocation.x - x;
			dy = gameLocation.y - y;
			if (dx * dx + dy * dy > r2) {
				continue;
			}
			int index = Arrays.binarySearch(exclude, e, c);
			if (index < 0) {
				if (foundCount > 0) {
					if (foundCount < number || c.compare(e, retval[smallestPointer]) > 0) {
						index = Arrays.binarySearch(retval, e, c);
						int insertIndex = -index - 2;
						System.arraycopy(retval, 1, retval, 0, insertIndex);
						retval[insertIndex] = e;
						foundCount++;
						if (smallestPointer > 0) {
							smallestPointer--;
						}
					}
				} else {
					retval[smallestPointer] = e;
					foundCount = 1;
				}
			}

		}

		if (foundCount < number) {
			HexTDCreep[] tempRetVal = new HexTDCreep[foundCount];
			System.arraycopy(retval, smallestPointer, tempRetVal, 0, foundCount);
			return tempRetVal;
		}
		return retval;
	}

	public static HexTDCreep findLargestSingle(Collection<HexTDCreep> enemies, double x, double y, double r, Comparator<HexTDCreep> c, HexTDCreep prefered, boolean exShielded) {
		double r2 = r * r;
		double dx, dy;
		Double gameLocation = null;
		if (prefered != null) {
			if (prefered.isValidTarget() && !(exShielded && prefered.isShielded())) {
				prefered.getGameLocation(gameLocation);
				dx = gameLocation.x - x;
				dy = gameLocation.y - y;
				if (dx * dx + dy * dy < r2) {
					return prefered;
				}
			}
		}
		HexTDCreep target = null;

		for (HexTDCreep e : enemies) {
			if (!e.isValidTarget() || (exShielded && e.isShielded())) {
				continue;
			}
			e.getGameLocation(gameLocation);
			dx = gameLocation.x - x;
			dy = gameLocation.y - y;
			if (dx * dx + dy * dy > r2) {
				continue;
			}
			if (c.compare(e, target) > 0) {
				target = e;
			}
		}
		return target;
	}
}
